package com.mySpringMvc.context;

import com.mySpringMvc.stereotype.Controller;
import com.mySpringMvc.web.bind.annotation.RequestMapping;
import com.mySpringMvc.web.constant.Const;
import com.mySpringMvc.web.method.HandlerMethod;
import com.mySpringMvc.web.servlet.HandlerAdapter;
import com.mySpringMvc.web.servlet.HandlerInterceptor;
import com.mySpringMvc.web.servlet.HandlerMapping;
import com.mySpringMvc.web.servlet.mvc.method.RequestMappingInfo;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * @program: mySSM
 * @description: 应用上下文
 * @author:wjp
 * @create: 2025-02-13 23:52
 **/
public class ApplicationContext {
    private Map<String, Object> baeaMap = new HashMap<String, Object>();

    public ApplicationContext() {
    }

    public ApplicationContext(String xmlPath){
        // 解析xml文件（组件扫描的所有bean对象，视图解析器，拦截器，处理器映射器，处理器适配器）

        // 使用dom4j读取xml文件
        SAXReader saxReader = new SAXReader();
        // xml配置文件文档对象
        Document document = null;
        try {
            document = saxReader.read(new File(xmlPath));
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

        // 获取根下beans标签中的component-scan标签（此处仅作演示，假设组件扫描仅一个包且不包含子包）
        Element componentScanElement = (Element) document.selectSingleNode("/beans/component-scan");
        Map<RequestMappingInfo,HandlerMethod> map =  componentScan(componentScanElement);

        // 视图解析器
        Element viewResolverElement = (Element) document.selectSingleNode("/beans/bean");
        createViewResolver(viewResolverElement);

        // 拦截器
        Element interceptorsElement = (Element) document.selectSingleNode("/beans/interceptors");
        createInterceptor(interceptorsElement);

        // 处理器映射器
        createHandlerMappings(Const.DEFAULT_PACKAGE,map);

        // 处理器适配器
        createHandlerAdapter(Const.DEFAULT_PACKAGE);


    }

    private Map<RequestMappingInfo,HandlerMethod> componentScan(Element componentScanElement) {
        HashMap<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodHashMap = new HashMap<>();
        String basePackage = componentScanElement.attributeValue("base-package");
        // 此时basePackage格式如com.wjp.oa.controller，通过将.替换为/并且拼接项目上下文路径，将其转为绝对路径，然后获取该路径下的所有class文件
        String replacedBasePackage = basePackage.replaceAll("\\.", "/");
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(replacedBasePackage).getPath();
        // 进行url解码
        absolutePath = URLDecoder.decode(absolutePath, Charset.defaultCharset());
        // 将该绝对路径转为文件夹类型的文件对象
        File dirFile = new File(absolutePath);
        // 获取该文件夹下的所有文件
        File[] files = dirFile.listFiles();
        for (File file : files) {
            // 此时的文件可能为class文件，可能不为class文件
            String fileName = file.getName();
            if (fileName.endsWith(".class")){
                // class文件，需要为其创建对象并放入beanMap中
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                // 将basePackage和className拼接获取完整类名
                String realClassName = basePackage + "." + className;
                try {
                    Class<?> clazz = Class.forName(realClassName);
                    // 如果类上包含@Controller注解，则进行实例化
                    Controller annotation = clazz.getAnnotation(Controller.class);
                    if (!Objects.isNull(annotation)){
                        Object o = clazz.newInstance();
                        //获取注解上配置的beanName，如果为默认的空字符串，则将类名首字母小写作为beanBame
                        String beanName = annotation.value();
                        if ("".equals(beanName)){
                            beanName = className.substring(0,1).toLowerCase() + className.substring(1);
                        }
                        // 放入beanMap中
                        this.baeaMap.put(beanName,o);

                        // 由于此处就遍历了所有的controller，因此直接在此进行处理器映射器的内部map初始化
                        // 获取当前类上的所有方法
                        Method[] declaredMethods = clazz.getDeclaredMethods();
                        for (Method declaredMethod : declaredMethods) {
                            // 判断类上是否有@Request注解
                            if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
                                // 将其转换为HandlerMethod对象（第一个参数为当前controller对象，第二个参数为当前方法）
                                HandlerMethod handlerMethod = new HandlerMethod(o, declaredMethod);
                                // 构造RequestInfo对象
                                RequestMappingInfo requestMappingInfo = new RequestMappingInfo();
                                RequestMapping requestMapping = declaredMethod.getAnnotation(RequestMapping.class);
                                // 从注解上获取请求路径
                                requestMappingInfo.setRequestURI(requestMapping.value()[0]);
                                // 从注解上获取请求方法
                                requestMappingInfo.setMethod(requestMapping.method().toString());
                                // 放入map中
                                requestMappingInfoHandlerMethodHashMap.put(requestMappingInfo,handlerMethod);
                            }
                        }


                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return requestMappingInfoHandlerMethodHashMap;
    }

    private void createViewResolver(Element viewResolverElement) {
        // 获取完整包路径
        String classPath = viewResolverElement.attributeValue("class");
        try {
            Class<?> clazz = Class.forName(classPath);
            Object o = clazz.newInstance();
            // 获取当前节点下的所有property标签子节点
            List<Element> propertyElements = viewResolverElement.elements("property");
            for (Element propertyElement : propertyElements) {
                String fieldName = propertyElement.attributeValue("name");
                String fieldValue = propertyElement.attributeValue("value");
                // 调用对应的set方法
                String fieldNameSetMethod = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
                Method setMethod = clazz.getDeclaredMethod(fieldNameSetMethod, String.class);
                setMethod.invoke(o,fieldValue);

                // 放入beanMap
                baeaMap.put(classPath.substring(classPath.lastIndexOf(".") + 1).substring(0,1).toLowerCase()
                        +classPath.substring(classPath.lastIndexOf(".") + 1).substring(1) ,o);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private void createInterceptor(Element interceptorsElement) {
        List<HandlerInterceptor> handlerInterceptors = new ArrayList<>();
        // 获取所有interceptors标签下的所有bean标签，即为所有的拦截器配置标签并遍历
        for (Element interceptorElement : interceptorsElement.elements("bean")) {
            // 获取类完整路径创建对象并放入beanMap
            String classpath = interceptorElement.attributeValue("class");
            try {
                Class<?> clazz = Class.forName(classpath);
                // 判断该类是否实现了HandlerInterceptor接口
                if (HandlerInterceptor.class.isAssignableFrom(clazz)){
                    Object o = clazz.newInstance();
                    // 判断类型是否为HandlerInterceptor
                    if (o instanceof HandlerInterceptor){
                        handlerInterceptors.add((HandlerInterceptor) o);
                    }
                }
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        this.baeaMap.put("interceptorList",handlerInterceptors);
    }

    private void createHandlerMappings(String defaultPackage, Map<RequestMappingInfo, HandlerMethod> map) {
        String replacedBasePackage = defaultPackage.replaceAll("\\.", "/");
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(replacedBasePackage).getPath();
        // 进行url解码
        absolutePath = URLDecoder.decode(absolutePath, Charset.defaultCharset());
        // 将该绝对路径转为文件夹类型的文件对象
        File dirFile = new File(absolutePath);
        // 获取该文件夹下的所有文件
        File[] files = dirFile.listFiles();
        for (File file : files) {
            // 此时的文件可能为class文件，可能不为class文件
            String fileName = file.getName();
            if (fileName.endsWith(".class")){
                // class文件，需要为其创建对象并放入beanMap中
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                // 将basePackage和className拼接获取完整类名
                String realClassName = defaultPackage + "." + className;
                try {
                    Class<?> clazz = Class.forName(realClassName);
                    // 判断该类是否实现了HandlerMapping接口
                    if(HandlerMapping.class.isAssignableFrom(clazz)){
//                        Object o = clazz.newInstance();
                        // 创建handlerMapping对象时需要同时初始化其内部map对象，因此调用有参构造方法
                        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(Map.class);
                        Object o = declaredConstructor.newInstance(map);
                        //获取注解上配置的beanName，如果为默认的空字符串，则将类名首字母小写作为beanBame
                        String clazzName = clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
                        String beanName = clazzName.substring(0,1).toLowerCase() + clazzName.substring(1);
                        // 放入beanMap中
                        this.baeaMap.put(beanName,o);

                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private void createHandlerAdapter(String defaultPackage) {
        String replacedBasePackage = defaultPackage.replaceAll("\\.", "/");
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(replacedBasePackage).getPath();
        // 进行url解码
        absolutePath = URLDecoder.decode(absolutePath, Charset.defaultCharset());
        // 将该绝对路径转为文件夹类型的文件对象
        File dirFile = new File(absolutePath);
        // 获取该文件夹下的所有文件
        File[] files = dirFile.listFiles();
        for (File file : files) {
            // 此时的文件可能为class文件，可能不为class文件
            String fileName = file.getName();
            if (fileName.endsWith(".class")){
                // class文件，需要为其创建对象并放入beanMap中
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                // 将basePackage和className拼接获取完整类名
                String realClassName = defaultPackage + "." + className;
                try {
                    Class<?> clazz = Class.forName(realClassName);
                    // 判断该类是否实现了HandlerAdapter接口
                    if (HandlerAdapter.class.isAssignableFrom(clazz)){
                        Object o = clazz.newInstance();
                        //获取注解上配置的beanName，如果为默认的空字符串，则将类名首字母小写作为beanBame
                        String clazzName = clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
                        String beanName = clazzName.substring(0,1).toLowerCase() + clazzName.substring(1);
                        // 放入beanMap中
                        this.baeaMap.put(beanName,o);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public Object getBean(String beanName) {
        return baeaMap.get(beanName);
    }
}
